#include "AspectEngine.h"

namespace agtk {

Model::Model(char* name, char* path) : Resource<Model>(name, path) 
{
	m_bLoaded = false;
	m_czName = name;
	m_czPath = path;
	int namelength = strlen(name);
	int pathlength = strlen(path);
	int totallength = namelength+pathlength+1;
	char* fullpath;
	fullpath = (char *)malloc(totallength);
	if ( fullpath != NULL )
	{
		strcpy(fullpath, path);
		strcat(fullpath, name);
	}
	m_czFullPath = fullpath;
	m_bLoaded=Load();
}

Model::~Model(void)
{
	m_triangles.Empty();
}

bool Model::Load()
{
	FILE* fp;
	fp = fopen(m_czFullPath, "rb");
	if (fp == NULL) {
		g_aspectEngine->GetLogger()->Log(LOGGER_ERROR, "Could not open model file for loading.", m_czName);
		return false;
	}

	fread(&m_version, sizeof(ModelVersion), 1, fp);
	
	m_header.title = ReadString(fp);
	m_header.description = ReadString(fp);
	m_header.author = ReadString(fp);
	m_header.defaultMaterialName = ReadString(fp);
	m_header.defaultMaterialPath = ReadString(fp);
	
	fread(&m_header.triangleCount, sizeof(unsigned int), 1, fp);
	
	unsigned int triCount = m_header.triangleCount;
	unsigned int i1 = 0;
	unsigned int i2 = 0;
	float posx = 0;
	float posy = 0;
	float posz = 0;
	float normx = 0;
	float normy = 0;
	float normz = 0;
	float u = 0;
	float v = 0;
	
	for(i1=0;i1<triCount;i1++) {
		m_triangles.Add(new Triangle());
		for(i2=0;i2<3;i2++) {
			fread(&posx, sizeof(float), 1, fp); // posx.
			fread(&posy, sizeof(float), 1, fp); // posy.
			fread(&posz, sizeof(float), 1, fp); // posz.
			fread(&normx, sizeof(float), 1, fp); // normx.
			fread(&normy, sizeof(float), 1, fp); // normy.
			fread(&normz, sizeof(float), 1, fp); // normz.
			fread(&u, sizeof(float), 1, fp); // u.
			fread(&v, sizeof(float), 1, fp); // v.
			m_triangles.GetLast()->m_vertices[i2]=new Vertex(posx, posy, posz, normx, normy, normz, u, v);
		}
		m_triangles.GetLast()->CalculateTangentSpace();
	}

	fclose(fp);
	
	// Log it.
	g_aspectEngine->GetLogger()->Log(LOGGER_INFO, "Model loaded: ", m_czName);
	
	m_currentMaterial = g_aspectEngine->GetMaterialManager()->Add(const_cast<char*>(m_header.defaultMaterialName.c_str()), const_cast<char*>(m_header.defaultMaterialPath.c_str()));

	return true;
}

void Model::Render()
{
	if (m_bLoaded) {
		if (m_currentMaterial->diffuseMap != NULL) {
			m_currentMaterial->diffuseMap->Bind(0);
			g_aspectEngine->GetShaderPredefines()->agtk_normalMap->Set1i(0);
		}
		if (m_currentMaterial->normalMap != NULL) {
			m_currentMaterial->normalMap->Bind(1);
			g_aspectEngine->GetShaderPredefines()->agtk_normalMap->Set1i(1);
		}
		if (m_currentMaterial->specularMap != NULL) {
			m_currentMaterial->specularMap->Bind(2);
			g_aspectEngine->GetShaderPredefines()->agtk_normalMap->Set1i(2);
		}
		if (m_currentMaterial->glowMap != NULL) {
			m_currentMaterial->glowMap->Bind(3);
			g_aspectEngine->GetShaderPredefines()->agtk_normalMap->Set1i(3);
		}

		m_currentMaterial->UseTextures(true);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,
						GL_TEXTURE_MIN_FILTER,
						GL_LINEAR_MIPMAP_LINEAR);
		
	
		g_aspectEngine->GetShaderPredefines()->agtk_diffuseColor->Set4f(
			m_currentMaterial->diffuseColor->r,
			m_currentMaterial->diffuseColor->g,
			m_currentMaterial->diffuseColor->b,
			m_currentMaterial->diffuseColor->a);

		g_aspectEngine->GetShaderPredefines()->agtk_falloffColor->Set4f(
			m_currentMaterial->falloffColor->r,
			m_currentMaterial->falloffColor->g,
			m_currentMaterial->falloffColor->b,
			m_currentMaterial->falloffColor->a);

		g_aspectEngine->GetShaderPredefines()->agtk_specularIntensity->Set1f(m_currentMaterial->specularIntensity);
		g_aspectEngine->GetShaderPredefines()->agtk_specularPower->Set1f(m_currentMaterial->specularPower);
		g_aspectEngine->GetShaderPredefines()->agtk_glowIntensity->Set1f(m_currentMaterial->glowIntensity);
		g_aspectEngine->GetShaderPredefines()->agtk_falloffIntensity->Set1f(m_currentMaterial->falloffIntensity);
		g_aspectEngine->GetShaderPredefines()->agtk_falloffPower->Set1f(m_currentMaterial->falloffPower);
		g_aspectEngine->GetShaderPredefines()->agtk_falloffDetail->Set1f(m_currentMaterial->falloffDetail);

		glBegin(GL_TRIANGLES);
		m_triangles.Iterate(true); // Iterate from the beginning of the list
		int vertCounter = 0;
		Vec3<float> tempVector;
		while (m_triangles.Iterate() != NULL) {
			while (vertCounter < 3) {
				tempVector = *m_triangles.GetCurrent()->m_vertices[vertCounter]->m_tangent;
				g_aspectEngine->GetShaderPredefines()->agtk_tangent->Set3f(
					tempVector.Get(0),
					tempVector.Get(1),
					tempVector.Get(2));
				tempVector = *m_triangles.GetCurrent()->m_vertices[vertCounter]->m_bitangent;
				g_aspectEngine->GetShaderPredefines()->agtk_bitangent->Set3f(
					tempVector.Get(0),
					tempVector.Get(1),
					tempVector.Get(2));
				glTexCoord2f(m_triangles.GetCurrent()->m_vertices[vertCounter]->m_texCoords->Get(0),
						     m_triangles.GetCurrent()->m_vertices[vertCounter]->m_texCoords->Get(1)); // Set vertex texture coords
				glNormal3f(m_triangles.GetCurrent()->m_vertices[vertCounter]->m_normal->Get(0),
						   m_triangles.GetCurrent()->m_vertices[vertCounter]->m_normal->Get(1),
						   m_triangles.GetCurrent()->m_vertices[vertCounter]->m_normal->Get(2)); // Set vertex normal
				glVertex3f(m_triangles.GetCurrent()->m_vertices[vertCounter]->m_pos->Get(0),
						   m_triangles.GetCurrent()->m_vertices[vertCounter]->m_pos->Get(1),
						   m_triangles.GetCurrent()->m_vertices[vertCounter]->m_pos->Get(2)); // Set vertex position
				vertCounter++;
			}
			vertCounter=0;
		}
		glEnd();

		m_currentMaterial->UseTextures(false);
	}
}

void Model::Render(Material* material) {/*
	if (m_bLoaded) {
		if (material->GetDiffuse()->HasTexture) {	
			glEnable(GL_TEXTURE_2D);
			glBindTexture(GL_TEXTURE_2D, material->GetDiffuse()->TextureID);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D,
							GL_TEXTURE_MIN_FILTER,
							GL_LINEAR_MIPMAP_LINEAR);
		}
	
		if (m_currentMaterial->GetDiffuse())
			glColor3f(
				material->GetDiffuse()->Red,
				material->GetDiffuse()->Green,
				material->GetDiffuse()->Blue);
		else
			glColor3f(1.0f, 1.0f, 1.0f);
	

		glBegin(GL_TRIANGLES);
		m_triangles.Iterate(true); // Iterate from the beginning of the list
		int vertCounter = 0;
		while (m_triangles.Iterate() != NULL) {
			while (vertCounter < 3) {
				glTexCoord2f(m_triangles.GetCurrent()->m_vertices[vertCounter]->m_texCoords->Get(0),
						     m_triangles.GetCurrent()->m_vertices[vertCounter]->m_texCoords->Get(1)); // Set vertex texture coords
				glNormal3f(m_triangles.GetCurrent()->m_vertices[vertCounter]->m_normal->Get(0),
						   m_triangles.GetCurrent()->m_vertices[vertCounter]->m_normal->Get(1),
						   m_triangles.GetCurrent()->m_vertices[vertCounter]->m_normal->Get(2)); // Set vertex normal
				glVertex3f(m_triangles.GetCurrent()->m_vertices[vertCounter]->m_pos->Get(0),
						   m_triangles.GetCurrent()->m_vertices[vertCounter]->m_pos->Get(1),
						   m_triangles.GetCurrent()->m_vertices[vertCounter]->m_pos->Get(2)); // Set vertex position
				vertCounter++;
			}
			vertCounter=0;
		}
		glEnd();

		glDisable(GL_TEXTURE_2D);
	}*/
}

Material* Model::GetMaterial() {
	return m_currentMaterial;
}

}